#ifndef _CALCULATE_H_
#define _CALCULATE_H_

#include <iostream>
#include <vector>
#include <cstdio>

/**
* @brief To construct a function library to complete a calculation.
* 
*/

#define DEFAULE 0
#define Isnum 1
#define Isope 2
#define Ispoint 3
#define Isblank 4

using namespace std;

/**
* @brief Define a struct to make record on the operations.
* 
*/
struct Doublenum
{
    double number = 0;
    int pointnum = 0;
    bool point = false;
    double fuhao = 1;
    int init = 0;
};

/**
* @brief To initialize the struct part.
* 
* @param part
*/
void clearpart( Doublenum & part )
{
    part.number = 0;
    part.point = false;
    part.pointnum = 0;
    part.fuhao = 1;
    part.init = 0;
}

/**
* @brief To print out the elements in vector arr.
* 
* @param arr
*/
template <typename T>
void Printout( const vector<T> & arr )
{
    int size = arr.size();
    for( int i = 0; i < size; i++ )
        cout << arr[i] ;
}

/**
* @brief To judge the type of the character.
* 
* @param ch
*/
int Judgech( const char & ch )
{
    int ret = DEFAULE;
    switch( ch )
    {
        case '+': case '-': case '*': case '/': case '(': case ')':
            ret = Isope; break;
        case '.': ret = Ispoint; break;
        case ' ': ret = Isblank; break;
        default:
            if( ch >= '0' && ch <= '9' )
                ret = Isnum;
    }
    return ret;
}

/**
* @brief To modify the struct part according to ch.
* 
* @param part, ch
*/
void  Completenum( Doublenum & part, const char & ch )
{
    if( part.point == false)
    {
        if( ch == '0' && part.number == 0 )
            ; // Do nothing.
        else
        {
            part.number = part.number * 10 + ch - '0';
        }
    }
    else
    {
        part.pointnum ++;
        double x = 1;
        for( int i = 0; i < part.pointnum; i++)
            x *= 0.1;
        part.number += x * (ch - '0');
    }
    part.init = 1;
}

/**
* @brief To add point to the number in part.
* 
* @param part
*/
bool Addpoint( Doublenum & part )
{
    bool corret = true;
    if( part.point )
    {
        corret = false;
    }
    else
    {
        part.point = true;
    }
    return corret;
}

/**
* @brief To judge whether the two vectors are totally operated.
* 
* @param ope, num
*/
bool Judgeempty( const vector<char> & ope, const vector<double> & num )
{
    bool corret = false;
    if( ope.size() == 0 && num.size() == 1 )
        corret = true;
    return corret;
}

/**
* @brief To put out the wrong information.
* 
* @param store
*/
void Puterror( const vector<char> & store )
{
    Printout( store );
    cout << " : Error." << endl;
}

/**
* @brief To judge the priority of the operation.
* 
* @param ch
*/
int Judgelevel( const char ch )
{
    int ret = 0;
    switch (ch)
    {
      case '+': case '-': ret = 1; break;
      case '*': case '/': ret = 2; break;
      case '(': case ')': ret = 3; break;
    }
    return ret;
}

/**
* @brief To make calculation on vector
* 
* @param num, ope
*/
bool Docalculate( vector<double> & num, vector<char> & ope )
{
    bool corret = true;
    if( num.size() < 2 || !ope.size() )
        corret = false;
    else if( Judgelevel( ope[ ope.size() - 1 ] ) == 3 )
        corret = false;
    else
    {
        int index = num.size() - 1;
        double x1, x2, x;
        x1 = num[ index - 1 ];
        x2 = num[ index ];
        num.pop_back();
        num.pop_back();
        switch( ope[ ope.size() - 1 ])
        {
            case '+': x = x1 + x2; break;
            case '-': x = x1 - x2; break;
            case '*': x = x1 * x2; break;
            case '/': 
                if( !x2 )
                {
                    corret = false;
                    break;
                }
                x = x1 / x2;
                break;
        }
        num.push_back( x );
        ope.pop_back();
    }
    return corret;
}

/**
* @brief To make different operations by ch.
* 
* @param num, ope, part, ch
*/
bool Getoperator( vector<char> & ope, vector<double> & num, Doublenum & part, const char & ch )
{
    bool corret = true;

    // Complete the calculation if enter ' '
    if( Judgech(ch) == Isblank )
    {
        if( part.init )
        {
            num.push_back( part.number * part.fuhao );
            clearpart( part );
        }
        while( !ope.empty() )
        {
            corret = Docalculate( num, ope );
            if( !corret )
                break;
        }
        if( num.size() != 1 )
            corret = false;
    }

    // Make negetive number.
    else if( ch == '-' && !part.init )
        part.fuhao *= -1;

    // Make different operation by operation entered.
    else if( Judgech(ch) == Isope )
    {
        if( ch != '(' && part.init )
        {
            num.push_back( part.number * part.fuhao );
            clearpart( part );
        }
        if( ope.empty() )
        {
            ope.push_back( ch );
        }
        else
        {
            char ch2 =  ope[ ope.size() - 1 ];
            if( ch == '(' )
                ope.push_back( ch );
            else if( ch == ')' )
            {
                while( ch2 != '(' && !ope.empty() )
                {
                    corret = Docalculate( num, ope );
                    if( !corret )
                        break;
                    if( !ope.empty() )
                        ch2 = ope[ ope.size() - 1 ];
                }
                if( ch2 != '(' )
                    corret = false;
                else
                {
                    ope.pop_back();
                }
            }
            else if( Judgelevel(ch) > Judgelevel(ch2) )
                ope.push_back( ch );
            else
            {
                while( !ope.empty() && Judgelevel(ch2) >= Judgelevel(ch) && ch2 != '(' )
                {
                    corret = Docalculate( num, ope );
                    if( corret == false )
                        break;
                    if ( !ope.empty() )
                        ch2 = ope[ ope.size() - 1 ];
                }
                ope.push_back( ch );
            }
        }
    }
    else
        corret = false;
    return corret;
}

#endif